package com.example.demo.controller;


import com.example.demo.model.LogInfo;
import com.example.demo.model.UserInfo;
import com.example.demo.service.LogService;
import com.example.demo.service.UserService;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class UserController {
    @Resource
    private UserService userService;

    @Resource
    private LogService logService;

    @Resource
    //事务管理器，负责管理事务的行为，比如开始，提交，回滚等操作
    private DataSourceTransactionManager transactionManager;
    @Resource
    //是对事务属性的相关定义，比如设置事务的隔离传播机制，超时时间等
    private TransactionDefinition transactionDefinition;

    // 在此方法中使用编程式的事务
    @RequestMapping("/add")
    public int add(UserInfo userInfo) {
        // 非空效验【验证用户名和密码不为空】
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        // 开启事务（获取事务）
        // 扩展：如果我单纯的开启了事务，而不进行提交事务或回滚事务，这时我的操作也会达到回滚的效果
        // 当你没有使用commit提交时，系统会认为没有提交事务，操作会自动回滚，因此不会数据持久化，这是一种保护机制，避免误提交！！
        TransactionStatus transactionStatus =
                transactionManager.getTransaction(transactionDefinition);
        int result = userService.add(userInfo);
        System.out.println("受影响的行数：" + result);
        //transactionManager.commit(transactionStatus);   // 提交事务
        //transactionManager.rollback(transactionStatus); // 回滚事务
        return result;
    }


    // 声明式事务，直接加 @Transactional
    //在进入该方法之前自动开启事务，方法结束后自动提交事务，如果遇到异常就自动回滚事务
    //注意：修饰方法时，该注解只能加到 Public修饰的方法上。修饰类时：表明该注解对该类中所有的 public ⽅法都⽣效
    @Transactional
    @RequestMapping("/add1")
    public int add1(UserInfo userInfo) {
        // 非空效验【验证用户名和密码不为空】
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        int result = userService.add(userInfo);
        System.out.println("受影响的行数：" + result);
        //int num = 10/0;
        return result;
    }


    //当使用声明式事务时，如果对异常进行了try..catch 处理，那么就不会进行事务回滚了 ！
    //解决方案1：将异常重新抛出去 throw e; 多余操作 0.0
    //解决方案2：手动回滚事务  TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    @Transactional
    @RequestMapping("/add2")
    public int add2(UserInfo userInfo) {
        // 非空效验【验证用户名和密码不为空】
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername())
                || !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        int result = userService.add(userInfo);
        System.out.println("受影响的行数：" + result);
        try {
            int num = 10 / 0;
        }catch (Exception e){
            // throw e;
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        return result;
    }

    /*==============  事务的传播机制示例  ===============*/
    // 以下示例是：方法 add3(PPT中所说的当前方法，即外部方法) 中 嵌套了 userService.add 和 logService.add(内部方法)
    // 就以下事务传播机制的设置(userService的add设置为REQUIRES_NEW，logService中的add设置为REQUIRED)，
    // 得到的结果是：添加用户成功(因为它自己新建了一个事务，日志异常造成的回滚不会影响到用户添加)，日志添加失败，因为LogService 中存在异常，事务进行回滚
    @Transactional(propagation = Propagation.REQUIRED) //默认就是 REQUIRED
    @RequestMapping("/add3")
    public int add3(UserInfo userInfo) {
        if (userInfo == null ||
                !StringUtils.hasLength(userInfo.getUsername()) ||
                !StringUtils.hasLength(userInfo.getPassword())) {
            return 0;
        }
        int userResult = userService.add(userInfo);
        System.out.println("添加用户：" + userResult);
        LogInfo logInfo = new LogInfo();
        logInfo.setName("添加用户的姓名");
        logInfo.setDescription("添加用户结果：" + userResult);
        int logResult = logService.add(logInfo);
        return userResult;
    }
}
